/*************************************************************************
    > File Name: face_container.cpp
    > Author: fanzhihui
    > Mail: 953106194@qq.com 
    > Created Time: Fri 14 Jun 2019 05:45:33 PM CST
 ************************************************************************/
#include "face_container.h"
#include<iostream>
#include "face_api_helper.h"
#include "config.h"
#include <future>

using namespace std;


bool KownFaceList::add(const string& key, const kown_face_info_t& face)
{
	bool ret = true;
	

	shared_ptr<kown_face_node_> node(new kown_face_node_());
	node->info = face;

	map<string, shared_ptr<kown_face_node_> >::iterator it = mapKownFace.find(key);
	if( it == mapKownFace.end() ) {
		mapKownFace[key] = node;
		if(!head) {
			head = node;
			tail = node;
		}else {
			tail->next = node;
			node->pre = tail;
			tail = node;
		}
	} else {
		ret = false;
	}

	return ret;
}
	

bool KownFaceList::find(const string& key,kown_face_info_t& out)
{
	bool ret = true;

	map<string, shared_ptr<kown_face_node_> >::iterator it = mapKownFace.find(key);
	if( it == mapKownFace.end() ) {
		ret = false;
	}else{
		out.features = it->second->info.features;
		out.face_key = it->second->info.face_key;
		out.face_id = it->second->info.face_id;
	}
	
	return ret;
}
	

bool KownFaceList::remove(const string& key,kown_face_info_t& del_face)
{
	bool ret = true;

	map<string,shared_ptr<kown_face_node_> >::iterator it = mapKownFace.find(key);
	if( it == mapKownFace.end() ){
		ret = false;
	}else{

		shared_ptr<kown_face_node_> del = it->second;
		if(head == del) {
			head.reset();
			tail.reset();
		}else if( tail = del) {
			tail = del->pre;
			tail->next.reset();
		}
        del_face = it->second->info;

		mapKownFace.erase(it);
	}


	return ret;
}


bool KownFaceList::update(const string& key,const kown_face_info_t & in)
{
	bool ret = true;


	map<string,shared_ptr<kown_face_node_> >::iterator it = mapKownFace.find(key);
	if( it == mapKownFace.end() ){
		ret = false;
	}else{
		it->second->info = in;
	}
	

	return ret;
}


void KownFaceList::clear()
{
	for( auto it = mapKownFace.begin(); it != mapKownFace.end(); ++it ) {
		it->second.reset();
	}
	for(auto ptr = head; !ptr; ) {
		if( ptr.get() != NULL && !ptr->next ){
			ptr->next->pre.reset();
			auto cur = ptr;
			ptr = ptr->next;
			cur->next.reset();
		}else{
			break;
		}
	}
	mapKownFace.clear();
	head.reset();
	tail.reset();
}

void KownFaceList::allFace(std::vector<kown_face_info_t>& all)
{

	std::map<string, shared_ptr<kown_face_node_> >::iterator it;
	for(it = mapKownFace.begin(); it != mapKownFace.end(); ++it ) {
	
		kown_face_info_t info = it->second->info;

		all.push_back(it->second->info);
	}

}


//FaceContainer* FaceContainer::instance = NULL;

FaceContainer::FaceContainer()
{
	threadPool = new ThreadPool(5);
}
	

FaceContainer::FaceContainer(const FaceContainer&)
{

}
	

FaceContainer& FaceContainer::operator=(const FaceContainer&)
{

}


void FaceContainer::Init(shared_ptr<FaceSerializeIf> serialize)
{
	faceSerial = serialize;
	vector<face_serial_info_t> serials; 
	faceSerial->UnSerialize(serials);
	
	std::lock_guard<std::mutex> lock(locker);

	for(uint32_t i = 0; i < serials.size(); i++) {
		kown_face_info_t face = serials[i].face;
		//face.dump();
		int op_flag = serials[i].op_flag;
		string key = Key(face.face_id,face.face_key);
		if( op_flag == SERIAL_ADD_FACE ){
			kownFaces.add(key,face);

			auto it = id2Keys.find(face.face_id);
			if( it == id2Keys.end() ){
				set<string> keys;
				keys.insert(face.face_key);
				id2Keys[face.face_id] = keys;
			} else{
				it->second.insert(face.face_key);
			}
		} else {
			kown_face_info_t del;
			kownFaces.remove(key,del);

			auto it = id2Keys.find(face.face_id);
			if( it != id2Keys.end() ){
				it->second.erase(face.face_key);
			}
		
		}
	}
	std::cout <<"FaceContainer init kown face db size:" << kownFaces.size() << std::endl;
}


string FaceContainer::Key(const string& face_id,const string& face_key)
{
	return face_id + face_key;
}

void FaceContainer::AddFace(const string& face_id, const string& face_key, const std::string& img_base64,bool overrid)
{
	std::vector<float> features;
	FaceApiHelper::getInstance()->get_face_feature_by_base64(img_base64,features);

	if(features.size() != 512){
		throw FaceContainerException("img have no features");
	}


	std::lock_guard<std::mutex> lock(locker);

	kown_face_info_t info(face_id,face_key,features);


	string key = Key(face_id,face_key);

	if( !kownFaces.add(key,info) ) {
		//
		if( overrid ) {
			if(!kownFaces.update(key,info)) {
				throw  FaceContainerException("add face exception");
			}
		} else {
			throw FaceContainerException("repeat add face");
		}
		
	}
	map<string,set<string> >::iterator it;
	it = id2Keys.find(face_id);
	if( it == id2Keys.end() ){
		set<string> keys;
		keys.insert(face_key);
		id2Keys[face_id] = keys;
	} else{
		it->second.insert(face_key);
	}

	std::cout << "all faces num:" << kownFaces.size() << std::endl; 

	face_serial_info_t serial;
	serial.face = info;
	serial.op_flag = SERIAL_ADD_FACE;
	faceSerial->Serialize(serial);
}


void FaceContainer::RemoveFace(const string& face_id)
{
	vector<kown_face_info_t> delFaces;

	std::lock_guard<std::mutex> lock(locker);
	map<string,set<string> >::iterator it;
	it = id2Keys.find(face_id);
	if( it == id2Keys.end() ){
		throw FaceContainerException("not found face id");
	} else {
		set<string>::iterator kit;
		for( kit = it->second.begin(); kit != it->second.end(); ++kit ) {
			string key = Key(face_id,*kit);
			kown_face_info_t del;
			if( !kownFaces.remove(key,del)) {
				throw FaceContainerException("not found face key");
			}
			delFaces.push_back(del);
		}
	}

	for( uint32_t i = 0; i < delFaces.size(); i++) {
		face_serial_info_t serial;
		serial.face = delFaces[i];
		serial.op_flag = SERIAL_DELETE_FACE;
		faceSerial->Serialize(serial);
	}
}
	
/*
void FaceContainer::ReplaceFace(uint64_t id,const std::vector<float> fs)
{
	kown_face_info_t info(id,fs);
	if( !kownFaces.update(id,info) ) {
		throw FaceContainerException("not found face");
	}
}*/



typedef struct score_info_{
	string face_id;
	string face_key;
	float score;
}score_info_t;


score_info_t max(const vector<score_info_t>& ss)
{
	score_info_t ret;
	float max_score = 0.000;
	float cur_score = max_score;
	for( uint32_t i = 0; i < ss.size(); i++){
		cur_score = ss[i].score;
		if(cur_score > max_score){
			max_score = cur_score;
			ret = ss[i];
		}
	}
	return ret;
}


score_info_t  get_max_score(const std::vector<kown_face_info_t>& all_face,vector<float>& features,uint32_t from,uint32_t to)
{
	auto p1 = std::chrono::high_resolution_clock::now();

	score_info_t ret;
	float max_score = 0.000;
	float cur_score = max_score;

	for( uint32_t i = from; i < to; i++ ) {
		cur_score = FaceApiHelper::getInstance()->compare_feature(features,all_face[i].features);
	   
		if( cur_score > max_score ) {
			max_score = cur_score;
			ret.face_id = all_face[i].face_id;
			ret.face_key = all_face[i].face_key;
		}
	}
	ret.score = max_score;

	auto p2 = std::chrono::high_resolution_clock::now();

	std::cout <<"get_max_score clock time:" <<
		(float)std::chrono::duration_cast<std::chrono::microseconds>(p2 - p1).count() / 1000 << std::endl;

	return ret;
}



	//匹配人脸，返回score
void FaceContainer::FindFace(const std::string& image, string& face_id,string& face_key,float& score)
{
    //std::chrono::time_point<std::chrono::high_resolution_clock> p1,p2,p3,p4;

	std::cout <<"FindFace" << std::endl;

	auto p1 = std::chrono::high_resolution_clock::now();


	std::vector<float> features;
	FaceApiHelper::getInstance()->get_face_feature_by_base64(image,features);
	if(features.size() != 512){
		throw FaceContainerException("img have no features");
	}

	auto p2 = std::chrono::high_resolution_clock::now();

	std::cout <<"get feature clock time:" <<
		(float)std::chrono::duration_cast<std::chrono::microseconds>(p2 - p1).count() / 1000 << std::endl;






	locker.lock();
	std::vector<kown_face_info_t> all_face;
	kownFaces.allFace(all_face);
	locker.unlock();


	auto p3 = std::chrono::high_resolution_clock::now();
	std::cout <<"get all face clock time:" <<
		(float)std::chrono::duration_cast<std::chrono::microseconds>(p3 - p2).count() / 1000 << std::endl;


	vector<std::future<score_info_t>> futures;
	uint32_t faces_per_task = Config::faces_per_async_task;
	uint32_t async_task_num = all_face.size() / faces_per_task;

	for( uint32_t i = 0; i < async_task_num; i++) {
	
		futures.push_back(threadPool->enqueue([&all_face,&features](uint32_t from,uint32_t to){
					return get_max_score(all_face,features,from,to);
					},
					i*faces_per_task,
					(i+1)*faces_per_task
					));

	}
	if( all_face.size() % faces_per_task != 0 ){
		futures.push_back(threadPool->enqueue([&all_face,&features](uint32_t from,uint32_t to){
					return get_max_score(all_face,features,from,to);
					},
					async_task_num*faces_per_task,
					all_face.size()
					));	
	}
	
	
	auto p3_0 = std::chrono::high_resolution_clock::now();
	std::cout <<"emplace clock time:" <<
		(float)std::chrono::duration_cast<std::chrono::microseconds>(p3_0 - p3).count() / 1000 << std::endl;



	auto p3_1 = std::chrono::high_resolution_clock::now();

	vector<score_info_t> ss;
	for( uint32_t i = 0; i < futures.size(); i++){
		ss.push_back(futures[i].get());
	}
	
	auto p3_2 = std::chrono::high_resolution_clock::now();
	std::cout <<"wait async clock time:" <<
		(float)std::chrono::duration_cast<std::chrono::microseconds>(p3_2 - p3_1).count() / 1000 << std::endl;

	score_info_t ms = max(ss);
	face_id = ms.face_id;
	face_key = ms.face_key;
	score = ms.score;

	
	auto p4 = std::chrono::high_resolution_clock::now();
	std::cout <<"compare all feature clock time:" <<
		(float)std::chrono::duration_cast<std::chrono::microseconds>(p4 - p3).count() / 1000 << std::endl;

}

void FaceContainer::ClearFace()
{
	std::lock_guard<std::mutex> lock(locker);
	faceSerial->Clear();
	id2Keys.clear();
	kownFaces.clear();
}


